package com.example.socket.service.imp;

import com.example.socket.aop.ServiceLimit;
import com.example.socket.aop.Servicelock;
import com.example.socket.common.Result;
import com.example.socket.mapper.SeckillMapper;
import com.example.socket.model.Seckill;
import com.example.socket.model.SeckillStatEnum;
import com.example.socket.model.SuccessKilled;
import com.example.socket.service.ISeckillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created with IDEA
 * author:QinWei
 * Date:2019/5/21
 * Time:9:06
 */
@Service("seckillService")
public class SeckillServiceImpl implements ISeckillService {

    @Resource
    private SeckillMapper seckillMapper;

    /**
     * 思考：为什么不用synchronized
     * service 默认是单例的，并发下lock只有一个实例
     */
    private Lock lock = new ReentrantLock(true);//互斥锁 参数默认false，不公平锁


    @Override
    public List<Seckill> getSeckillList() {
        return seckillMapper.findAll();
    }

    @Override
    public Seckill getById(long seckillId) {
        return seckillMapper.findOne(seckillId);
    }

    @Override
    public Long getSeckillCount(long seckillId) {
        return seckillMapper.findCount(seckillId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSeckill(long seckillId) {
        seckillMapper.deleteSeckill(seckillId);
        seckillMapper.updateSeckill(seckillId);
    }
    @Override
    @ServiceLimit
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckil(long seckillId,long userId) {
        //校验库存
        Long number =  seckillMapper.queryNumber(seckillId);
        if(number>0){
            //扣库存
            seckillMapper.updateCountSeckill(seckillId);
            //创建订单
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            seckillMapper.save(killed);
            //支付
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result  startSeckilLock(long seckillId, long userId) {
        try {
            lock.lock();
            /**
             * 1)这里、不清楚为啥、总是会被超卖101、难道锁不起作用、lock是同一个对象
             * 2)来自热心网友 zoain 的细心测试思考、然后自己总结了一下,事物未提交之前，锁已经释放(事物提交是在整个方法执行完)，导致另一个事物读取到了这个事物未提交的数据，也就是传说中的脏读。建议锁上移
             * 3)给自己留个坑思考：为什么分布式锁(zk和redis)没有问题？(事实是有问题的，由于redis释放锁需要远程通信，不那么明显而已)
             * 4)2018年12月35日，更正一下,之前的解释（脏读）可能给大家一些误导,数据库默认的事务隔离级别为 可重复读(repeatable-read)，也就不可能出现脏读
             * 哪个这个级别是只能是幻读了？分析一下：幻读侧重于新增或删除，这里显然不是，那这里到底是什么，给各位大婶留个坑~~~~
             */
            Long number =  seckillMapper.queryNumber(seckillId);
            if(number>0){
                seckillMapper.updateCountSeckill(seckillId);
                SuccessKilled killed = new SuccessKilled();
                killed.setSeckillId(seckillId);
                killed.setUserId(userId);
                killed.setState(Short.parseShort(number+""));
                seckillMapper.save(killed);
            }else{
                return Result.error(SeckillStatEnum.END);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }
    @Override
    @Servicelock
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckilAopLock(long seckillId, long userId) {
        //来自码云码友<马丁的早晨>的建议 使用AOP + 锁实现
        Long number =  seckillMapper.queryNumber(seckillId);
        if(number>0){
            seckillMapper.updateCountSeckill(seckillId);
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState(Short.parseShort(number+""));
            seckillMapper.save(killed);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }
    //注意这里 限流注解 可能会出现少买 自行调整
    @Override
    @ServiceLimit
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckilDBPCC_ONE(long seckillId, long userId) {
        //单用户抢购一件商品或者多件都没有问题
        Long number =  seckillMapper.queryNumber(seckillId);
        if(number>0){
            seckillMapper.updateCountSeckill(seckillId);
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            seckillMapper.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }
    /**
     * SHOW STATUS LIKE 'innodb_row_lock%';
     * 如果发现锁争用比较严重，如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckilDBPCC_TWO(long seckillId, long userId) {
        //单用户抢购一件商品没有问题、但是抢购多件商品不建议这种写法
        int count = seckillMapper.updateSeckillNumber(seckillId);
        if(count>0){
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            seckillMapper.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckilDBOCC(long seckillId, long userId, long number) {
        Seckill kill = seckillMapper.findOne(seckillId);
        //if(kill.getNumber()>0){
        if(kill.getNumber()>=number){//剩余的数量应该要大于等于秒杀的数量
            //乐观锁
            int count = seckillMapper.updateSeckillVersion(number,seckillId,kill.getVersion());
            if(count>0){
                SuccessKilled killed = new SuccessKilled();
                killed.setSeckillId(seckillId);
                killed.setUserId(userId);
                killed.setState((short)0);
                seckillMapper.save(killed);
                return Result.ok(SeckillStatEnum.SUCCESS);
            }else{
                return Result.error(SeckillStatEnum.END);
            }
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    public Result startSeckilTemplate(long seckillId, long userId, long number) {
        return null;
    }

}
